package cn.zyq.demo.service.user.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.math.MathUtil;
import cn.hutool.core.util.StrUtil;
import cn.zyq.demo.beans.sys.SysAdmin;
import cn.zyq.demo.mapper.user.UserMapper;

import cn.zyq.demo.beans.user.User;
import cn.zyq.demo.service.user.UserService;
import cn.zyq.demo.system.common.BaseService;
import cn.zyq.demo.system.common.PageParam;
import cn.zyq.demo.system.config.consts.redis.RedisH5Keys;
import cn.zyq.demo.system.config.consts.system.RegularConst;
import cn.zyq.demo.system.config.consts.system.SystemConfig;
import cn.zyq.demo.system.config.redis.RedisService;
import cn.zyq.demo.system.config.sms.SmsConfig;
import cn.zyq.demo.system.config.weixin.WxConfig;
import cn.zyq.demo.system.enums.core.AuditTypeEnum;
import cn.zyq.demo.system.enums.core.ResponseCodeEnum;
import cn.zyq.demo.system.exception.BusinessException;
import cn.zyq.demo.system.pay.core.client.PayClient;
import cn.zyq.demo.system.pay.core.client.PayCommonResult;
import cn.zyq.demo.system.pay.core.client.dto.PayOrderTransferReqDTO;
import cn.zyq.demo.system.pay.core.client.dto.PayOrderTransferRespDTO;
import cn.zyq.demo.system.pay.core.client.impl.alipay.AlipayPayClientConfig;
import cn.zyq.demo.system.ry.CacheConstants;
import cn.zyq.demo.system.ry.util.ServletUtils;
import cn.zyq.demo.system.utils.Sample;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.tea.TeaModel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 用户表
 * 2023-04-02 09:09:22
 */

@Slf4j
@Service
public class UserServiceImpl extends BaseService implements UserService {
    @Autowired
    private RedisService redisService;

    @Autowired
    private UserMapper userMapper;



    @Override
    public String getCacheKey(String key) {
        return CacheConstants.LONGIN_H5_TOKEN_KEY + key;
    }

    @Override
    public User getFromCache(String key) {
        return this.redisService.getCacheObject(this.getCacheKey(key));
    }

    @Override
    public void setToCache(User user) {
        String cacheKey = this.getCacheKey(user.getToken());
        this.redisService.setCacheObject(cacheKey, user);
        this.redisService.expire(user.getToken(), SystemConfig.timeoutH5, TimeUnit.HOURS);
    }

    @Override
    public void deleteForCache(String key) {
        this.redisService.deleteObject(this.getCacheKey(key));
    }

    /**
     * 创建新的token，并更新到缓存、数据库中
     *
     * @param user
     * @return 返回新建的token
     */
    private String updateToken(User user) {
        User storeInfo = this.getUpdateInstance(User.class);
        storeInfo.setUserID(user.getUserID());
        String token = this.uuid();
        storeInfo.setToken(token);
        user.setToken(token);
        this.setToCache(user);
        this.userMapper.updateByPrimaryKeySelective(storeInfo);
        return token;
    }

    @Override
    public PageInfo<User> list(PageParam pageParam, SysAdmin admin) {
        PageHelper.startPage(pageParam.getPage(), pageParam.getPageSize());
        List<User> dbList = this.userMapper.list(pageParam.getKv());
        return new PageInfo<>(dbList);
    }

    @Override
    public void add(User param, SysAdmin admin) {
        Integer id = param.getUserID();
        User addInfo = this.getCreateInstance(User.class, admin);
        addInfo.setUserID(id);
        this.userMapper.insertSelective(addInfo);
    }

    @Override
    public void update(User param, SysAdmin admin) {
        Integer id = param.getUserID();
        User storeInfo = this.getUpdateInstance(User.class, admin);
        storeInfo.setUserID(id);
        this.userMapper.updateByPrimaryKeySelective(storeInfo);
    }

    @Override
    public void del(String serviceID, SysAdmin admin) {
        User dbInfo = this.detail(serviceID);
        Integer id = dbInfo.getUserID();
        this.deleteLogic(id, admin, this.userMapper);
    }

    @Override
    public User detail(String serviceID, SysAdmin admin) {
        return this.detail(serviceID);
    }

    @Override
    public User detail(String serviceID) {
        User dbInfo = this.getByServiceID(serviceID, User.class, this.userMapper);
        this.checkLogicExist(dbInfo);
        return dbInfo;
    }

    @Override
    public User detailByID(Integer userID) {
        return this.userMapper.selectByPrimaryKey(userID);
    }

    @Override
    public User getLoginUser() {
        HttpServletRequest request = ServletUtils.getRequest();
        String token = request.getHeader("token");
        User user = null;
        if (StringUtils.isNotBlank(token)) {
            user = this.getFromCache(token);
            if (user == null) {
                // 用token查用户信息，查不到要抛异常
                User paramUser = new User();
                paramUser.setToken(token);
                user = this.userMapper.selectOne(paramUser);
                // log.info("token不正确，请在网站上登录自己的帐号，然后进入个人设置页面扫描二维码获取token");
                if (user == null) {
                    throw new BusinessException(ResponseCodeEnum.H5_NO_LOGIN);
                }
            }
        } else {
            throw new BusinessException(ResponseCodeEnum.H5_NO_LOGIN);
        }
        this.setToCache(user);
        return user;
    }

    @Override
    public User wxMaLogin(String code) {
        if (StringUtils.isBlank(code)) {
            throw new BusinessException(ResponseCodeEnum.INVALID_PARAM);
        }
        String sessionKey = null;
        String openId = null;
        try {
            WxMaJscode2SessionResult result = WxConfig.wxMaService().getUserService().getSessionInfo(code);
            sessionKey = result.getSessionKey();
            openId = result.getOpenid();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        if (StringUtils.isBlank(sessionKey) || StringUtils.isBlank(openId)) {
            log.error("微信登录,调用官方接口失败：{}", code);
            throw new BusinessException(ResponseCodeEnum.ERROR, "调用微信方失败");
        }
        User paramUser = new User();
        paramUser.setWxMaOpenid(openId);
        User user = this.userMapper.selectOne(paramUser);
        String token = this.uuid();
        if (user != null) {
            this.deleteForCache(user.getToken());
            user.setToken(token);
            this.userMapper.updateByPrimaryKeySelective(user);
        } else {
            user = new User();
            this.setCreateBaseData(user, user);
            user.setToken(token);
            user.setWxMaOpenid(openId);
            user.setCreateTime(LocalDateTime.now());
            this.userMapper.insertSelective(user);
            user = this.userMapper.selectOne(paramUser);
            //设置用户编码
            this.userMapper.setCodeByUserID(user.getUserID());
        }
        this.setToCache(user);
        return user;
    }

    @Override
    public Object getPhone(String code, User loginUser) {
        log.info("【请求开始】绑定手机号码,请求参数，code:{}", code);
        WxMaPhoneNumberInfo phoneNumberInfo = null;
        try {
            phoneNumberInfo = WxConfig.wxMaService().getUserService().getPhoneNoInfo(code);
        } catch (Exception e) {
            log.error("获取手机号码失败,获取微信绑定的手机号码出错：{}", code);
            log.error(e.getMessage(), e);
            throw new BusinessException(ResponseCodeEnum.ERROR, "通过微信获取手机号失败");
        }
        String phone = phoneNumberInfo.getPhoneNumber();
        loginUser.setPhone(phone);
        if (this.userMapper.updateByPrimaryKeySelective(loginUser) == 0) {
            log.error("获取手机号码,更新用户信息出错,id：{}", loginUser.getUserID());
            throw new BusinessException(ResponseCodeEnum.ERROR, "手机号码更新失败");
        }
        Map<Object, Object> data = new HashMap<Object, Object>();
        data.put("phone", phone);

        log.info("【请求结束】绑定手机号码,响应结果：{}", JSONObject.toJSONString(data));
        return this.userMapper.selectByPrimaryKey(loginUser.getUserID());
    }

    @Override
    public User updateInfo(User loginUser, User user) {
        log.info("【请求开始】修改用户信息,请求参数,loginUser:{},user:{}", loginUser, user);
        User updateInfo = new User();
        updateInfo.setUserID(loginUser.getUserID());
        updateInfo.setNickName(user.getNickName());
        updateInfo.setGender(user.getGender());
        updateInfo.setAvatar(user.getAvatar());
        int i = this.userMapper.updateByPrimaryKeySelective(updateInfo);
        if (i == 0) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "用户信息更新失败");
        }
        return this.userMapper.selectByPrimaryKey(loginUser.getUserID());
    }


    @Override
    public int addWallet(BigDecimal amount, Integer userID) {
        return this.userMapper.updateWallet(amount, userID);
    }

    @Override
    public int subWallet(BigDecimal amount, Integer userID) {
        return this.userMapper.subWallet(amount, userID);
    }

    @Override
    public int smsCode(String phone) {
        if (StrUtil.isEmpty(phone) || phone.length() != 11 || !phone.matches(RegularConst.PHONE_RULE)) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "没有获取到正确的手机号");
        }

        String smsCodeKey = RedisH5Keys.SMSCODE_KEY(phone);
        String existedSmsCode = redisService.getCacheObject(smsCodeKey);
        String smsCode = this.getSmsVerifyCode();
        //如果验证码已存在
        if (StringUtils.isNotEmpty(existedSmsCode)) {
            Long expireTime = redisService.redisTemplate.opsForValue().getOperations().getExpire(smsCodeKey);
            long lastTime = 60 * 3 - expireTime;
            //三分钟内验证码有效，1分钟到3分钟之间，用户可以继续输入验证码，也可以重新获取验证码，新的验证码将覆盖旧的
            if (lastTime > 60 && expireTime > 0) {
                //调用第三方平台发短信，只有短信发送成功了，才能将短信验证码保存到redis
                log.info("此处调用短信发送逻辑......");
                this.sendAliSms(phone, smsCode);
                redisService.redisTemplate.opsForValue().set(smsCodeKey, smsCode, 60 * 3, TimeUnit.SECONDS);
                log.info("短信验证码：{}", smsCode);
            }
            //一分钟之内不得多次获取验证码
            if (lastTime < 60) {
                throw new BusinessException(ResponseCodeEnum.ERROR, "操作过于频繁，请一分钟之后再次点击发送");
            }
            return 1;
        } else {
            //发送短信验证码
            log.info("验证码不存在，重新调用");
            this.sendAliSms(phone, smsCode);
            log.info("短信验证码：{}", smsCode);
            redisService.redisTemplate.opsForValue().set(smsCodeKey, smsCode, 60 * 3, TimeUnit.SECONDS);
            return 1;
        }
    }


//    @Override
//    @Transactional
//    public UserCash withdraw(UserCash param, User loginUser, HttpServletRequest request) {
//        try {
//            //10 代表10秒这个key直接过期
//            if (redisService.redisTemplate.opsForValue().setIfAbsent("SysUserLock"+loginUser.getUserID(), loginUser.getUserID(), Duration.ofSeconds(10))) {
//                //检查参数
//                if (StringUtils.isBlank(param.getPrice().toString())) {
//                    throw new BusinessException("请输入提现金额");
//                }
//                User user = this.userMapper.selectByPrimaryKey(loginUser.getUserID());
//                if (StringUtils.isBlank(user.getAliAccount())) {
//                    throw new BusinessException("未绑定支付宝账号，请先绑定支付宝");
//                }
//                if (param.getPrice().compareTo(user.getWallet()) > 0) {
//                    throw new BusinessException("钱包余额不足");
//                }
//                //修改用户钱包余额
//                User updateInstance = this.getUpdateInstance(User.class);
//                updateInstance.setUserID(loginUser.getUserID());
//                updateInstance.setWallet(user.getWallet().subtract(param.getPrice()));
//                this.setUpdateBaseData(updateInstance, loginUser);
//                this.userMapper.updateByPrimaryKeySelective(updateInstance);
//                //新增提现记录表
//                param.setUserID(loginUser.getUserID());
//                param.setState(AuditTypeEnum.AUDITING.getValue());
//                param.setCashName(user.getNickName());
//                param.setCashAccount(user.getAliAccount());
//                this.setCreateBaseData(param, loginUser);
//                int i = this.userCashMapper.insertSelective(param);
//                if (i == 0) {
//                    throw new BusinessException("新增提现记录失败");
//                }
//
//                PayOrderTransferReqDTO dto = new PayOrderTransferReqDTO();
//                String userIP = request.getRemoteAddr();
//                dto.setUserIp(userIP);
//                dto.setNotifyUrl(null);
//                dto.setUserAccount(user.getAliAccount());
//                dto.setUname(user.getAccountName());
//                dto.setOutTradeNo(param.getServiceID());
//                dto.setTitle("推一推APP提现");
//                dto.setAmount(MathUtil.yuanToCent(param.getPrice().doubleValue()));
//                log.info("组装提现参数 {}", dto);
//                PayClient client = AlipayPayClientConfig.getPayClientByChannelId(param.getChannelId());
//                PayCommonResult<PayOrderTransferRespDTO> result  = client.unifiedTransfer(dto);
//                log.info("提现结束 {}", result);
//                PayOrderTransferRespDTO data = result.getData();
//                if (data.getTradeStatus().equals("false")){
//                    throw new BusinessException("提现失败"+ result.getApiMsg());
//                }
//                //新增余额变更记录
//                UserBalance userBalance = new UserBalance(
//                        loginUser.getUserID(),
//                        user.getWallet().subtract(param.getPrice()),
//                        user.getWallet(),
//                        param.getPrice(),
//                        param.getUserCashID(),
//                        UserBalanceStatusEnum.TX.getValue(),
//                        user.getUserID(),
//                        "申请提现"
//                );
//                this.setCreateBaseData(userBalance,loginUser);
//                this.userBalanceMapper.insertSelective(userBalance);
//            }
//        }finally {
//            // 解锁
//            redisService.redisTemplate.delete("SysUserLock"+loginUser.getUserID());
//        }
//        return param;
//    }


    /**
     * 随机获取6位短信数字验证码
     *
     * @return
     */
    public static String getSmsVerifyCode() {
        Random random = new Random();
        String code = "";
        for (int i = 0; i < 6; i++) {
            int rand = random.nextInt(10);
            code += rand;
        }
        return code;
    }

    public static void sendAliSms(String phone, String smsCode) {
        log.info("调用阿里短信接口开始 {}", smsCode);
        try {
            com.aliyun.dysmsapi20170525.Client client = Sample
                    .createClient(SmsConfig.accessKeyId, SmsConfig.accessKeySecret);
            com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
                    .setSignName("招财猫")
                    .setTemplateCode(SmsConfig.templateCode)
                    .setPhoneNumbers(phone)
                    .setTemplateParam("{\"code\":\"" + smsCode + "\"}");
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            com.aliyun.dysmsapi20170525.models.SendSmsResponse resp = client.sendSmsWithOptions(sendSmsRequest, runtime);
            com.aliyun.teaconsole.Client.log(com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
            log.info("调用阿里短信接口结束{}", com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
        } catch (Exception e) {
            throw new BusinessException(ResponseCodeEnum.ERROR, "短信发送失败");
        }
    }
}
